// V1
#include "abckit_wrapper.h"
#include "cstring"
#include "../abc_commons.h"

namespace
{

    namespace V1
    {
        static auto g_impl = AbckitGetApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
        static auto g_implI = AbckitGetInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
        static auto g_implM = AbckitGetModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
        static auto g_implG = AbckitGetGraphApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
        static auto g_dynG = AbckitGetIsaApiDynamicImpl(ABCKIT_VERSION_RELEASE_1_0_0);
        static auto abckitArktsModifyApi = AbckitGetArktsModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
        static auto abckitArktsInspectApi = AbckitGetArktsInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);

        AbckitFile *openAbc(const std::string &path)
        {
            return g_impl->openAbc(path.c_str(), strlen(path.c_str()));
        }

        void writeAbc(AbckitFile *file, const std::string &path)
        {
            g_impl->writeAbc(file, path.c_str(), strlen(path.c_str()));
        }

        std::string getModuleName(AbckitCoreModule *m)
        {
            return std::move(g_implI->abckitStringToString(g_implI->moduleGetName(m)));
        }

        std::string getNamespaceName(AbckitCoreNamespace *n)
        {
            return std::move(g_implI->abckitStringToString(g_implI->namespaceGetName(n)));
        }

        std::string getClassName(AbckitCoreClass *klass)
        {
            return std::move(g_implI->abckitStringToString(g_implI->classGetName(klass)));
        }

        std::string getFunctionName(AbckitCoreFunction *f)
        {
            return std::move(g_implI->abckitStringToString(g_implI->functionGetName(f)));
        }

        std::string getNodeName(abc::CoreNode coreNode)
        {
            if (auto *cls = abc::getAs<AbckitCoreClass>(coreNode))
            {
                return getClassName(cls);
            }

            if (auto *module = abc::getAs<AbckitCoreModule>(coreNode))
            {
                return getModuleName(module);
            }

            if (auto *ns = abc::getAs<AbckitCoreNamespace>(coreNode))
            {
                return getNamespaceName(ns);
            }

            return "";
        }

        bool isModuleNode(abc::CoreNode coreNode)
        {
            return abc::getAs<AbckitCoreModule>(coreNode) != nullptr;
        }

        bool isNamespaceNode(abc::CoreNode coreNode)
        {
            return abc::getAs<AbckitCoreNamespace>(coreNode) != nullptr;
        }

        bool isClassNode(abc::CoreNode coreNode)
        {
            return abc::getAs<AbckitCoreClass>(coreNode) != nullptr;
        }

        bool isFunctionNode(abc::CoreNode coreNode)
        {
            return abc::getAs<AbckitCoreFunction>(coreNode) != nullptr;
        }

        abc::AbcNodeType getNodeType(abc::CoreNode coreNode)
        {
            if (isModuleNode(coreNode))
            {
                return abc::AbcNodeType::MODULE;
            }
            if (isNamespaceNode(coreNode))
            {
                return abc::AbcNodeType::NAMESPACE;
            }
            if (isClassNode(coreNode))
            {
                return abc::AbcNodeType::CLASS;
            }
            if (isFunctionNode(coreNode))
            {
                return abc::AbcNodeType::FUNCTION;
            }

            return abc::AbcNodeType::UNKOWN;
        }

        std::string iGetOpcodeType(AbckitIsaApiDynamicOpcode opcode)
        {
            return abc::getEnumTypeName(opcode);
        }

        std::string getNodePath(abc::AbcNodePtr abcNode)
        {
            return abcNode->path.module + ":" + abcNode->path.package;
        }

        std::string getFuncPath(abc::AbcFunctionPtr abcFunction)
        {
            return getNodePath(abcFunction->parentNode) + ":" + abcFunction->name;
        }
        std::string getImportDescriptorName(AbckitCoreImportDescriptor *i)
        {
            return std::move(g_implI->abckitStringToString(g_implI->importDescriptorGetName(i)));
        }

        AbckitBasicBlock *getFunctionStartBlock(AbckitGraph *graph)
        {
            AbckitBasicBlock *startBB = g_implG->gGetStartBasicBlock(graph);
            std::vector<AbckitBasicBlock *> succBBs;
            g_implG->bbVisitSuccBlocks(startBB, &succBBs, [](AbckitBasicBlock *bb, void *succBB)
                                       {
                        auto *succs = reinterpret_cast<std::vector<AbckitBasicBlock *> *>(succBB);
                        succs -> emplace_back(bb);
                        return true; });
            return succBBs[0];
        }

        void enumerateInstUsers(AbckitInst *inst, const InstUserCallback &cb)
        {
            g_implG->iVisitUsers(inst, (bool *)(&cb), [](AbckitInst *user, void *data)
                                 {
                const auto &cb = *((InstUserCallback *)data);
                cb(user);
                return true; });
        }

        std::string getInstName(AbckitInst *inst)
        {
            return std::move(g_implI->abckitStringToString(g_implG->iGetString(inst)));
        }

        AbckitApiAdapter createApi()
        {
            AbckitApiAdapter api{};
            // io操作
            api.io.openAbc = openAbc;
            api.io.writeAbc = writeAbc;
            api.io.closeFile = g_impl->closeFile;

            // node
            api.node.fileEnumerateModules = g_implI->fileEnumerateModules;
            api.node.moduleEnumerateNamespaces = g_implI->moduleEnumerateNamespaces;
            api.node.moduleEnumerateTopLevelFunctions = g_implI->moduleEnumerateTopLevelFunctions;
            api.node.moduleEnumerateClasses = g_implI->moduleEnumerateClasses;
            api.node.namespaceEnumerateTopLevelFunctions = g_implI->namespaceEnumerateTopLevelFunctions;
            api.node.namespaceEnumerateNamespaces = g_implI->namespaceEnumerateNamespaces;
            api.node.namespaceEnumerateClasses = g_implI->namespaceEnumerateClasses;
            api.node.classEnumerateMethods = g_implI->classEnumerateMethods;
            api.node.isModuleNode = isModuleNode;
            api.node.isNamespaceNode = isNamespaceNode;
            api.node.isClassNode = isClassNode;
            api.node.isFunctionNode = isFunctionNode;
            api.node.getNodeType = getNodeType;

            // import
            api.import.moduleEnumerateImports = g_implI->moduleEnumerateImports;
            api.import.importDescriptorGetImportedModule = g_implI->importDescriptorGetImportedModule;
            api.import.iGetImportDescriptor = g_dynG->iGetImportDescriptor;

            // function
            api.function.functionIsStatic = g_implI->functionIsStatic;
            api.function.functionIsAnonymous = g_implI->functionIsAnonymous;
            api.function.functionGetModule = g_implI->functionGetModule;
            api.function.functionGetFile = g_implI->functionGetFile;
            api.function.functionSetGraph = g_implM->functionSetGraph;

            // string
            api.string.abckitStringToString = g_implI->abckitStringToString;
            api.string.moduleGetName = g_implI->moduleGetName;
            api.string.namespaceGetName = g_implI->namespaceGetName;
            api.string.classGetName = g_implI->classGetName;
            api.string.functionGetName = g_implI->functionGetName;
            api.string.importDescriptorGetName = g_implI->importDescriptorGetName;
            api.string.createString = g_implM->createString;
            api.string.getModuleName = getModuleName;
            api.string.getNamespaceName = getNamespaceName;
            api.string.getClassName = getClassName;
            api.string.getFunctionName = getFunctionName;
            api.string.getNodeName = getNodeName;
            api.string.getNodePath = getNodePath;
            api.string.getFuncPath = getFuncPath;
            api.string.getImportDescriptorName = getImportDescriptorName;
            api.string.iGetString = g_implG->iGetString;
            api.string.getInstName = getInstName;

            // graph操作
            api.graph.destroyGraph = g_impl->destroyGraph;
            api.graph.createGraphFromFunction = g_implI->createGraphFromFunction;
            api.graph.gVisitBlocksRpo = g_implG->gVisitBlocksRpo;

            // inst操作
            api.inst.bbGetGraph = g_implG->bbGetGraph;
            api.inst.bbGetFirstInst = g_implG->bbGetFirstInst;
            api.inst.iGetPrev = g_implG->iGetPrev;
            api.inst.iGetNext = g_implG->iGetNext;
            api.inst.iGetFunction = g_implG->iGetFunction;
            api.inst.iGetOpcode = g_dynG->iGetOpcode;
            api.inst.bbAddInstFront = g_implG->bbAddInstFront;
            api.inst.iInsertBefore = g_implG->iInsertBefore;
            api.inst.iInsertAfter = g_implG->iInsertAfter;
            api.inst.iGetOpcodeType = iGetOpcodeType;
            api.inst.getFunctionStartBlock = getFunctionStartBlock;
            api.inst.gGetStartBasicBlock = g_implG->gGetStartBasicBlock;
            api.inst.bbVisitSuccBlocks = g_implG->bbVisitSuccBlocks;
            api.inst.iVisitUsers = g_implG->iVisitUsers;
            api.inst.enumerateInstUsers = enumerateInstUsers;

            // isa
            api.isa.iCreateLdexternalmodulevar = g_dynG->iCreateLdexternalmodulevar;
            api.isa.iCreateThrowUndefinedifholewithname = g_dynG->iCreateThrowUndefinedifholewithname;
            api.isa.iCreateLdobjbyname = g_dynG->iCreateLdobjbyname;
            api.isa.iCreateCallthis0 = g_dynG->iCreateCallthis0;
            return api;
        }

        static const AbckitApiAdapter instance = createApi();
    } // namespace
}

AbckitApiAdapter const *createAbckitAdapter(abc::AbckitVersion version)
{
    switch (version)
    {
    case abc::AbckitVersion::V1:
        return &V1::instance;
    default:
        throw std::invalid_argument("Unknown abckit version");
    }
}